#include "stdafx.hpp"
#include "ColligereAudioDbTreeCtrl.hpp"
#include "ColligereApp.hpp"

//////////////////////////////////////////////////
// col::AlbumTreeItemData
//////////////////////////////////////////////////

col::AlbumTreeItemData::AlbumTreeItemData() : mArtistId(0), 
mAlbumId(0), mYear(0)
{ 
}

col::AlbumTreeItemData::AlbumTreeItemData ( DatabaseItemId artistId, 
										   DatabaseItemId albumId, 
										   const wxString &rYear ) 
										   : mArtistId ( artistId ), 
										   mAlbumId ( albumId ), mYear(0)
{ 
	wxASSERT ( mArtistId > 0 && mAlbumId > 0 );
	rYear.ToULong ( &mYear );
}

col::AlbumTreeItemData::~AlbumTreeItemData()
{
}

col::DatabaseItemId col::AlbumTreeItemData::GetArtistId() const
{ 
	return mArtistId; 
}

col::DatabaseItemId col::AlbumTreeItemData::GetAlbumId() const 
{ 
	return mAlbumId; 
}

unsigned long col::AlbumTreeItemData::GetYear() const
{
	return mYear;
}

bool col::AlbumTreeItemData::operator == 
( const col::AlbumTreeItemData &rData ) const
{
	return ( mArtistId == rData.mArtistId && mAlbumId == rData.mAlbumId );
}

//////////////////////////////////////////////
// col::ArtistTreeItemData
//////////////////////////////////////////////

col::ArtistTreeItemData::ArtistTreeItemData ( const Artist &rArtist ) 
: mrArtist ( rArtist )
{ 

}

col::ArtistTreeItemData::~ArtistTreeItemData() 
{ 
}

wxUint32 col::ArtistTreeItemData::GetArtistId() const 
{ 
	return mrArtist.GetId();
}

const wxString &col::ArtistTreeItemData::GetArtist() const 
{ 
	return mrArtist.GetArtist(); 
}

const wxString &col::ArtistTreeItemData::GetCountry() const 
{ 
	return mrArtist.GetCountry();
}

bool col::ArtistTreeItemData::operator == 
( const ArtistTreeItemData &rData ) const
{
	return GetArtistId() == rData.GetArtistId() && GetArtist() ==
		rData.GetArtist() && rData.GetCountry() == rData.GetCountry();
}

/////////////////////////////////////////
// ColligereAudioDatabaseTreeCtrl
/////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS ( ColligereAudioDatabaseTreeCtrl, col::DatabaseTreeCtrlBase );

ColligereAudioDatabaseTreeCtrl::ColligereAudioDatabaseTreeCtrl()
{
}

ColligereAudioDatabaseTreeCtrl::~ColligereAudioDatabaseTreeCtrl() 
{ 
}

void ColligereAudioDatabaseTreeCtrl::Initialize()
{
	using namespace col;
	
	SetBrowseBy ( BrowseBy::Artist );

	Bind ( wxEVT_COMMAND_TREE_ITEM_EXPANDING, 
		&ColligereAudioDatabaseTreeCtrl::OnItemExpanding );
	Bind ( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 
		&ColligereAudioDatabaseTreeCtrl::OnItemCollapsing );
	Bind ( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, 
		&ColligereAudioDatabaseTreeCtrl::OnGetItemToolTip );

	typedef App::IconIndex ii;

	wxImageList *item_images ( new wxImageList ( 16, 16, true ) );

	const wxInt32 cd_indx = item_images->Add ( wxGetApp().GetIcon ( ii::Cd ) );
	mImageList [ "cd|digipak|digibook|compact disc" ] = cd_indx;

	const wxInt32 vinyl_indx = item_images->Add ( wxGetApp().GetIcon ( ii::Vinyl ) );
	mImageList [ "7\" vinyl|12\" vinyl|vinyl|lp|dlp" ] = vinyl_indx;

	const wxInt32 folder_indx = item_images->Add ( wxGetApp().GetIcon ( ii::FolderClosed ) );
	mImageList [ "folder closed" ] = folder_indx;

	const wxInt32 folder_opened_indx = item_images->Add ( wxGetApp().GetIcon ( ii::FolderOpened ) );
	mImageList [ "folder opened" ] = folder_opened_indx;

	AssignImageList ( item_images );

	AddRoot ( _("Collection"), folder_indx );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendItem 
( const col::DatabaseItemBase &rItem, bool select )
{
	typedef ColligereAudioDatabaseTreeCtrl::BrowseBy bb;

	try
	{
		const col::Album &album = dynamic_cast < const col::Album & > ( rItem );

		wxTreeItemId item;

		switch ( GetBrowseBy() )
		{
		case bb::Artist:
			{
				item = AppendInBrowseByArtistMode ( GetRootItem(), album );
				break;
			}
		case bb::Country:
			{
				item = AppendInBrowseByCountryMode ( GetRootItem(), album );
				break;
			}
		case bb::Format:
			{
				item = AppendInBrowseByFormatMode ( GetRootItem(), album );
				break;
			}
		case bb::Genre:
			{
				item = AppendInBrowseByGenreMode ( GetRootItem(), album );
				break;
			}
		case bb::Year:
			{
				item = AppendInBrowseByYearMode ( GetRootItem(), album );
				break;
			}
		case bb::Tag:
			{
				using namespace std;

				col::string_deque tags ( album.GetTags() );

				if ( tags.empty() )
				{
					AppendInBrowseByTagMode ( GetRootItem(), _("[None]"), album );
				}
				else
				{
					foreach ( const wxString &tag, tags )
					{
						AppendInBrowseByTagMode ( GetRootItem(), tag, album );
					}
				}
				break;
			}
		default:
			wxFAIL;
			item = AppendInBrowseByArtistMode ( GetRootItem(), album );
			break;
		}
		if ( item.IsOk() )
		{
			if ( select )
			{
				wxTreeCtrl::SelectItem ( item );
			}
			return item;
		}
	}
	catch ( std::exception &rErr )
	{
		wxFAIL_MSG ( rErr.what() );
	}
	return wxTreeItemId();
}

void ColligereAudioDatabaseTreeCtrl::SelectItem 
( const col::DatabaseItemBase &rItem, bool select )
{
	if ( const col::Artist *artist = dynamic_cast 
		< const col::Artist * > ( &rItem ) )
	{
		const wxTreeItemId artist_id ( FindArtist ( GetRootItem(),
			artist->GetArtist(), artist->GetCountry() ) );

		if ( artist_id.IsOk() )
		{
			wxTreeCtrl::SelectItem ( artist_id, select );
		}
	}
	else if ( const col::Album *album = dynamic_cast 
		< const col::Album * > ( &rItem ) )
	{
		const wxTreeItemId album_id ( FindAlbum ( GetRootItem(), 
			col::AlbumTreeItemData ( album->GetParentId(),
			album->GetId(), album->GetYear() ) ) );

		if ( album_id.IsOk() )
		{
			wxTreeCtrl::SelectItem ( album_id, select );
		}
	}
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::UpdateItem 
( const col::DatabaseItemBase &rItem )
{
	const wxTreeItemId sel_id ( GetSelection() );
	wxASSERT ( IsAlbumItem ( sel_id ) );

	const wxTreeItemId new_item ( AppendItem ( rItem, false ) );

	wxTreeItemId parent ( GetItemParent ( sel_id ) );

	Delete ( sel_id );

	while ( parent.IsOk() )
	{
		const wxTreeItemId grand_parent ( GetItemParent ( parent ) );

		if ( !ItemHasChildren ( parent ) && parent != GetRootItem() )
		{
			Delete ( parent );
		}
		parent = grand_parent;
	}
	SortChildren ( GetItemParent ( new_item ) );
	wxTreeCtrl::SelectItem ( new_item );

	return new_item;
}

const wxString ColligereAudioDatabaseTreeCtrl::FormatToDefaultIfEmpty 
( const wxString &rValue )
{
	if ( rValue.empty() )
	{
		return _("[None]");
	}
	return rValue;
}

const wxString ColligereAudioDatabaseTreeCtrl::FormatToDefaultIfZero
( const wxString &rValue )
{
	if ( rValue == "0" )
	{
		return _("[None]");
	}
	return rValue;
}

const wxString ColligereAudioDatabaseTreeCtrl::FormatAlbumToText
( const col::Album &rAlbum ) const
{
	using namespace boost;

	const col::Artist &artist = dynamic_cast 
		< const col::Artist & > ( rAlbum.GetParent() ); 

	const wxString &year = rAlbum.GetYear();

	const wxString &artist_str = artist.GetArtist();
	wxASSERT ( !artist_str.empty() );
	const wxString &album_str = rAlbum.GetAlbum();
	wxASSERT ( !album_str.empty() );

	if ( !year.empty() && year != "0" )
	{
		return str ( format ( "%1% - %2% (%3%)" ) % artist_str % album_str % year );
	}
	return str ( format ( "%1% - %2%" ) % artist_str % album_str );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendInBrowseByCountryMode 
( const wxTreeItemId &rRoot, const col::Album &rAlbum )
{
	const col::Artist &artist = dynamic_cast 
		< const col::Artist & > ( rAlbum.GetParent() ); 

	const wxString country ( FormatToDefaultIfEmpty ( artist.GetCountry() ) );
	
	wxTreeItemId parent ( FindItem ( rRoot, country ) );

	if ( !parent.IsOk() )
	{
		parent = wxTreeCtrl::AppendItem ( rRoot, country, 
			mImageList["folder closed"] );
	}
	return AppendInBrowseByArtistMode ( parent, rAlbum );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendInBrowseByTagMode 
( const wxTreeItemId &rRoot, const wxString &rTag, const col::Album &rAlbum )
{
	using namespace boost;

	wxTreeItemId parent ( FindItem ( rRoot, rTag ) );

	if ( !parent.IsOk() )
	{
		parent = wxTreeCtrl::AppendItem ( rRoot, rTag, mImageList["folder closed"] );
	}
	return wxTreeCtrl::AppendItem ( parent, FormatAlbumToText ( rAlbum ), 
		GetImageIndexForType ( rAlbum.GetFormat() ), -1, 
		new col::AlbumTreeItemData ( rAlbum.GetParentId(), 
		rAlbum.GetId(), rAlbum.GetYear() ) );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendInBrowseByArtistMode 
( const wxTreeItemId &rRoot, const col::Album &rAlbum )
{
	using namespace boost;

	const col::Artist &artist = dynamic_cast 
		< const col::Artist & > ( rAlbum.GetParent() ); 

	const wxString &artist_name = artist.GetArtist();
	const wxString &country = artist.GetCountry();

	wxASSERT ( !artist_name.empty() );
	
	wxTreeItemId parent ( FindArtist ( rRoot, artist_name, country ) );

	if ( !parent.IsOk() )
	{
		parent = wxTreeCtrl::AppendItem 
			( rRoot, artist_name, mImageList["folder closed"] );

		SetItemData ( parent, new col::ArtistTreeItemData ( artist ) );
	}

	const wxString &year = rAlbum.GetYear();

	if ( !year.empty() && year != '0' )
	{
		return wxTreeCtrl::AppendItem ( parent, str ( format ( "%1% (%2%)" ) % 
			rAlbum.GetAlbum() % year ), GetImageIndexForType ( rAlbum.GetFormat() ), -1, 
			new col::AlbumTreeItemData ( rAlbum.GetParentId(), 
			rAlbum.GetId(), rAlbum.GetYear() ) );
	}
	return wxTreeCtrl::AppendItem ( parent, rAlbum.GetAlbum(), GetImageIndexForType 
		( rAlbum.GetFormat() ), -1, new col::AlbumTreeItemData 
		( rAlbum.GetParentId(), rAlbum.GetId(), rAlbum.GetYear() ) );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendInBrowseByFormatMode 
( const wxTreeItemId &rRoot, const col::Album &rAlbum )
{
	using namespace boost;

	const wxString album_format ( FormatToDefaultIfEmpty ( rAlbum.GetFormat() ) );

	wxTreeItemId parent ( FindItem ( rRoot, album_format ) );

	if ( !parent.IsOk() )
	{
		parent = wxTreeCtrl::AppendItem ( rRoot, album_format, 
			mImageList["folder closed"] );
	}
	return wxTreeCtrl::AppendItem ( parent, FormatAlbumToText ( rAlbum ), 
		GetImageIndexForType ( rAlbum.GetFormat() ), -1, 
		new col::AlbumTreeItemData ( rAlbum.GetParentId(), 
		rAlbum.GetId(), rAlbum.GetYear() ) );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendInBrowseByGenreMode 
( const wxTreeItemId &rRoot, const col::Album &rAlbum )
{
	using namespace boost;

	const wxString genre ( FormatToDefaultIfEmpty ( rAlbum.GetGenre() ) );

	wxTreeItemId parent ( FindItem ( rRoot, genre ) );

	if ( !parent.IsOk() )
	{
		parent = wxTreeCtrl::AppendItem ( rRoot, genre, mImageList["folder closed"] );
	}
	return wxTreeCtrl::AppendItem ( parent, FormatAlbumToText ( rAlbum ), 
		GetImageIndexForType ( rAlbum.GetFormat() ), -1, 
		new col::AlbumTreeItemData ( rAlbum.GetParentId(),
		rAlbum.GetId(), rAlbum.GetYear() ) );
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::AppendInBrowseByYearMode 
( const wxTreeItemId &rRoot, const col::Album &rAlbum )
{
	using namespace boost;

	const col::Artist &artist = dynamic_cast 
		< const col::Artist & > ( rAlbum.GetParent() ); 

	const wxString year ( FormatToDefaultIfZero 
		( FormatToDefaultIfEmpty ( rAlbum.GetYear() ) ) );

	wxTreeItemId parent ( FindItem ( rRoot, year ) );

	if ( !parent.IsOk() )
	{
		parent = wxTreeCtrl::AppendItem 
			( rRoot, year, mImageList["folder closed"] );
	}
	return wxTreeCtrl::AppendItem ( parent, str ( format ( "%1% - %2%" ) % 
		artist.GetArtist() % rAlbum.GetAlbum() ), 
		GetImageIndexForType ( rAlbum.GetFormat() ), -1, 
		new col::AlbumTreeItemData ( rAlbum.GetParentId(), 
		rAlbum.GetId(), rAlbum.GetYear() ) );
}

void ColligereAudioDatabaseTreeCtrl::ClearItemList()
{
	// Delete everything except the root item (i.e. "Collection").
	DeleteChildren ( GetRootItem() );
}

wxInt32 ColligereAudioDatabaseTreeCtrl::GetImageIndexForType 
( const wxString &rType ) const
{
	for ( ImageMap::const_iterator it = mImageList.begin();
		it != mImageList.end(); ++it )
	{
		if ( wxRegEx ( it->first, wxRE_ICASE ).Matches ( rType ) )
		{
			return it->second;
		}
	}
	return -1;
}

void ColligereAudioDatabaseTreeCtrl::DeleteItem ( const col::TreeItemData &rItemData )
{
	try
	{
		const col::AlbumTreeItemData &data = dynamic_cast 
			< const col::AlbumTreeItemData & > ( rItemData );

		const wxTreeItemId album_id ( FindAlbum ( GetRootItem(), data ) );

		if ( !album_id.IsOk() ) return;

		wxTreeItemId parent_id ( GetItemParent ( album_id ) );

		Delete ( album_id );

		while ( parent_id.IsOk() )
		{
			wxTreeItemId grand_parent_id ( GetItemParent ( parent_id ) );

			if ( !ItemHasChildren ( parent_id ) && parent_id != GetRootItem() )
			{
				Delete ( parent_id );
			}
			parent_id = grand_parent_id;
		}
	}
	catch ( std::exception &rErr )
	{
		wxLogError ( rErr.what() );
	}
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::FindAlbum 
( const wxTreeItemId &rRoot, const col::AlbumTreeItemData &rItemData )
{
	wxTreeItemIdValue cookie;
	wxTreeItemId node ( GetFirstChild ( rRoot, cookie ) );
	
	while ( node.IsOk() )
	{
		const col::AlbumTreeItemData *data = 
			dynamic_cast < const col::AlbumTreeItemData * > ( GetItemData ( node ) );

		if ( data && rItemData == *data )
		{
			return node;
		}
		if ( ItemHasChildren ( node ) )
		{
			node = GetFirstChild ( node, cookie );
		}
		else
		{
			while ( !GetNextSibling ( node ).IsOk() && node != rRoot )
			{
				node = GetItemParent ( node );
			}
			node = GetNextSibling ( node );
		}		
	}
	return node;
}

wxTreeItemId ColligereAudioDatabaseTreeCtrl::FindArtist 
( const wxTreeItemId &rRoot, const wxString &rArtist,
 const wxString &rCountry )
{
	wxTreeItemIdValue cookie;
	wxTreeItemId node ( GetFirstChild ( rRoot, cookie ) );
	
	while ( node.IsOk() && node != rRoot )
	{
		const col::ArtistTreeItemData *data = dynamic_cast 
			< const col::ArtistTreeItemData * > ( GetItemData ( node ) );

		if ( data && data->GetArtist() == rArtist && data->GetCountry() == rCountry )
		{
			return node;
		}
		if ( ItemHasChildren ( node ) )
		{
			node = GetFirstChild ( node, cookie );
		}
		else
		{
			while ( node.IsOk() && !GetNextSibling ( node ).IsOk() && node != rRoot )
			{
				node = GetItemParent ( node );
			}
			node = GetNextSibling ( node );
		}		
	}
	return node;
}

bool ColligereAudioDatabaseTreeCtrl::IsArtistItem ( const wxTreeItemId &rItem ) const
{
	if ( rItem.IsOk() )
	{
		return dynamic_cast < col::ArtistTreeItemData * > 
			( GetItemData ( rItem ) ) != 0;
	}
	return 0;
}

bool ColligereAudioDatabaseTreeCtrl::IsAlbumItem ( const wxTreeItemId &rItem ) const
{
	if ( rItem.IsOk() )
	{
		return dynamic_cast < col::AlbumTreeItemData * > 
			( GetItemData ( rItem ) ) != 0;
	}
	return 0;
}

wxInt32 ColligereAudioDatabaseTreeCtrl::OnCompareItems
( const wxTreeItemId &rItem1, const wxTreeItemId &rItem2 )
{
	if ( GetItemText ( rItem1 ) == _("[None]") ) return -1;
	if ( GetItemText ( rItem2 ) == _("[None]") ) return 1;
	
	if ( const col::AlbumTreeItemData *item1 = 
		dynamic_cast < const col::AlbumTreeItemData * > 
		( GetItemData (  rItem1 ) ) )
	{
		const col::AlbumTreeItemData *item2 = 
			dynamic_cast < const col::AlbumTreeItemData * > 
			( GetItemData ( rItem2 ) );

		wxASSERT ( item2 );

		if ( item1->GetYear() == item2->GetYear() ) return 0;
		if ( item1->GetYear() > item2->GetYear() ) return 1;
		else return -1;
	}
	return wxTreeCtrl::OnCompareItems ( rItem1, rItem2 );
}

///////////////////////////////////////////
// Event handlers
///////////////////////////////////////////

void ColligereAudioDatabaseTreeCtrl::OnItemExpanding ( wxTreeEvent &rEvent )
{
	if ( rEvent.GetItem().IsOk() )
	{
		SetItemImage ( rEvent.GetItem(), mImageList["folder opened"] );
	}
}

void ColligereAudioDatabaseTreeCtrl::OnItemCollapsing ( wxTreeEvent &rEvent )
{
	if ( rEvent.GetItem().IsOk() )
	{
		SetItemImage ( rEvent.GetItem(), mImageList["folder closed"] );
	}
}

void ColligereAudioDatabaseTreeCtrl::OnGetItemToolTip ( wxTreeEvent &rEvent )
{
	using namespace boost;

	if ( rEvent.GetItem() != GetRootItem() )
	{
		const size_t child_count = GetChildrenCount ( rEvent.GetItem() );

		if ( IsArtistItem ( rEvent.GetItem() ) && child_count > 0 )
		{
			rEvent.SetToolTip ( str ( format ( "%1% %2%" ) % child_count % 
				( child_count == 1 ? _("Album") : _("Albums") ) ) );
		}
	}
}